﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Xml.Linq;
using System.Data;
using System.Data.SqlClient;

namespace XEvents.Library.Targets
{
    public class Target_RingBuffer : TargetData
    {
        #region Constructor

        private Target_RingBuffer(SqlServer server, EventSessionTarget parent)
            : base(server, parent)
        {        }


        internal static TargetData GetTargetData(SqlServer server, EventSessionTarget parent)
        {
            Target_RingBuffer rb = new Target_RingBuffer(server, parent);
            return rb;
        }

        protected override void InitalizeTarget()
        {
            targetinfo = InitalizeTable();
            targetinfo.TableName = "EventInformation";
        }

        #endregion

        private DataTable InitalizeTable()
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("Id");
            dt.Columns.Add("Name");
            dt.Columns.Add("Package");
            dt.Columns.Add("Version");
            dt.Columns.Add("TimeStamp");

            foreach (string s in DataColumns)
            {
                dt.Columns.Add(s);
            }
            return dt;
        }
        protected override void LoadData()
        {
            ChangeStatus(LoadStatus.Requesting_Data);

            string data = Parent.Parent.Server.ServerConnection.ExecuteScalar(string.Format(SqlCommandStrings.TargetData, Parent.parent.Name, Parent.Name.Split('.').ElementAt(1))).ToString();
            XDocument targetdata = XDocument.Parse(data);

            ChangeStatus(LoadStatus.Processing_Data);

            try
            {
                ProcessXML(targetdata);
            }
            catch  // Removes last node which will cause exception if incomplete due to overbuffering.
            {
                targetdata.Elements().Elements().Last().Remove();
                ProcessXML(targetdata);
            }

            //targetinfo.BeginLoadData();
            if (events.Count > 0)
            {
                int i = 0;
                while (!bw.CancellationPending)
                {
                    if (i == 5)
                        targetinfo.BeginLoadData();

                    ProcessEvent(events[i]);
                    i++;
                    if (i == 1)
                        this.TargetInfoChanged();

                    if (i % 50 == 0)
                    {
                        bw.ReportProgress((int)((float)i / (float)events.Count * 100));
                    }
                    if (i >= events.Count)
                        break;
                    //if (i % 1000 == 0)
                    //{

                    //    i = 0;
                    //}
                }
                targetinfo.EndLoadData();
            }

            ChangeStatus(LoadStatus.Completed);

        }
        private void ProcessXML(XDocument targetdata)
        {
            this.eventspersec = (int)targetdata.Elements().ElementAt(0).Attribute("eventsPerSec");
            this.processingtime = (int)targetdata.Elements().ElementAt(0).Attribute("processingTime");
            this.totaleventsprocessed = (int)targetdata.Elements().ElementAt(0).Attribute("totalEventsProcessed");
            this.eventcount = (int)targetdata.Elements().ElementAt(0).Attribute("eventCount");
            this.droppedcount = (int)targetdata.Elements().ElementAt(0).Attribute("droppedCount");
            this.memoryused = (int)targetdata.Elements().ElementAt(0).Attribute("memoryUsed");

            this.events = new FiredEventCollection(
                    (from e in targetdata.Elements()
                     from fe in e.Elements()
                     select new FiredEvent
                     {
                         name = (string)fe.Attribute("name"),
                         package = (string)fe.Attribute("package"),
                         id = (string)fe.Attribute("id"),
                         version = (string)fe.Attribute("version"),
                         timestamp = (string)fe.Attribute("timestamp"),
                         payloaddata = new EventPayloadItemCollection((
                             from pc in fe.Descendants("data")
                             select new EventPayloadItem(this.Server)
                             {
                                 payloaditemtype = "Column",
                                 name = (string)pc.Attribute("name"),
                                 package = (string)pc.Attribute("package"),
                                 type_name = (string)pc.Element("type").Attribute("name"),
                                 type_package = (string)pc.Element("type").Attribute("package"),
                                 value = (string)pc.Element("value").Value,
                                 text = (string)pc.Element("text").Value
                             }).Union<EventPayloadItem>(
                             from pa in fe.Descendants("action")
                             select new EventPayloadItem(this.Server)
                             {
                                 payloaditemtype = "Action",
                                 name = (string)pa.Attribute("name"),
                                 package = (string)pa.Attribute("package"),
                                 type_name = (string)pa.Element("type").Attribute("name"),
                                 type_package = (string)pa.Element("type").Attribute("package"),
                                 value = (string)pa.Element("value").Value,
                                 text = (string)pa.Element("text").Value
                             }))
                     }).ToList());

        }

        #region Private Methods

        private List<string> DataColumns
        {
            get
            {
                List<string> result = new List<string>();

                // collect event columns
                result.AddRange(
                    (from s in Server.sessiondata.server_event_sessions
                     from e in Server.sessiondata.server_event_session_events
                     from p in Server.MetaData.packages
                     from c in Server.MetaData.object_columns
                     where s.name == this.Parent.Parent.name
                         && e.event_session_id == s.event_session_id
                         && p.name == e.package
                         && c.object_package_guid == p.guid
                         && c.object_name == e.name
                        && c.column_type != "readonly"
                     select c.name).ToList());

                // collect any action columns
                result.AddRange(
                     (from s in Server.sessiondata.server_event_sessions
                      from a in Server.sessiondata.server_event_session_actions
                      where s.name == this.Parent.Parent.name
                          && a.event_session_id == s.event_session_id
                      select a.name).ToList());

                return result.Distinct().ToList();
            }
        }

        private void ProcessEvent(object o)
        {
            DataRow row = targetinfo.NewRow();
            FiredEvent firedevent = (FiredEvent)o;

            row["Id"] = firedevent.Id;
            row["Name"] = firedevent.Name;
            row["Package"] = firedevent.Package;
            row["Version"] = firedevent.Version;
            row["TimeStamp"] = firedevent.Timestamp;
            foreach (EventPayloadItem epi in firedevent.EventPayloadItemCollection)
            {
                if (epi.Text == string.Empty)
                    row[epi.Name] = epi.Value;
                else
                    row[epi.Name] = epi.Text;
            }

            lock (locker)
                targetinfo.Rows.Add(row);
            //this.events.Add(firedevent);
        }

        #endregion




        internal FiredEventCollection events;
        internal int eventspersec;
        internal int processingtime;
        internal int totaleventsprocessed;
        internal int eventcount;
        internal int droppedcount;
        internal int memoryused;

        public FiredEventCollection Events
        {
            get { return events; }
        }

        public System.Xml.XmlDocument GetEventXml()
        {
            return Utilities.GetSerializedXml(typeof(FiredEventCollection), this.Events);
        }

        public int EventsPerSecond
        {
            get { return eventspersec; }
        }
        
        public int ProcessingTime
        {
            get { return processingtime; }
        }

        public int TotalEventsProcessed
        {
            get { return totaleventsprocessed; }
        }

        public int EventCount
        {
            get { return eventcount; }
        }

        public int DroppedCount
        {
            get { return droppedcount; }
        }

        public int MemoryUsed
        {
            get { return memoryused; }
        }

        public System.Data.DataTable GetEventsAsDataTable()
        {
            IEnumerable<string> q = (from t in this.Events
                                     from e in t.EventPayloadItemCollection
                                     select e.Name).Distinct();

            System.Data.DataTable dt = new System.Data.DataTable("EventInformation");

            dt.Columns.Add("Id");
            dt.Columns.Add("Name");
            dt.Columns.Add("Package");
            dt.Columns.Add("Version");
            dt.Columns.Add("TimeStamp");

            foreach (string s in q)
            {
                dt.Columns.Add(s);
            }

            foreach (FiredEvent fe in this.Events)
            {
                System.Data.DataRow dr = dt.Rows.Add();
                dr["Id"] = fe.Id;
                dr["Name"] = fe.Name;
                dr["Package"] = fe.Package;
                dr["Version"] = fe.Version;
                dr["TimeStamp"] = fe.Timestamp;
                foreach (EventPayloadItem epi in fe.EventPayloadItemCollection)
                {
                    dr[epi.Name] = epi.Value;
                }
                //dt.Rows.Add(dr);

            }
            return dt;
        }


        /*
<event name="" package="" id="" version="" timestamp="">
  <data name="">
    <type name="" package="" />
    <value> </value>
    <text />
  </data>
  <action name="" package="">
    <type name="" package="" />
    <value>  </value>
    <text />
  </action>
</event>
*/
    }
}